﻿#include "OperatorsLoad.h"
#include "PickDataProvider/PickedDataProvider.h"
#include "PickDataProvider/GUIPickInfo.h"
#include "OperatorsInterface/GraphEventOperator.h"
#include "GUIFrame/MainWindow.h"
#include "GUIDialog/GUILoadDialog/GUILoadConstraintDialog.h"
#include "GUIDialog/GUILoadDialog/GUILoadPowerDialog.h"
#include "GUIDialog/GUILoadDialog/GUILoadHVACNoiseDialog.h"
#include "GUIDialog/GUILoadDialog/GUILoadPointForceDialog.h"
#include "GUIDialog/GUILoadDialog/GUILoadPickSubSysDialog.h"
#include "GUIDialog/GUILoadDialog/GUILoadMonopoleDialog.h"

#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsFEModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEAFace.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysCavity.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysBeam.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsTriangles.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractElement.h"
#include "FITK_Component/FITKAcousticsPhysics/FITKAcousticsPhysics.h"
#include "FITK_Component/FITKAcousticsPhysics/FITKAcousticsLoadManager.h"
#include "FITK_Component/FITKAcousticsPhysics/FITKAcousticsAbstractLoad.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKMessage.h"
#include "updateBrowserTable.h"
#include <QAction>
#include <QDialog>

namespace ModelOper
{
    bool OperatorsLoad::execGUI()
    {
        if (!_emitter)return false;
        //获取动作名称
        QString actionName = _emitter->objectName();
        bool actionOK = false;
        //拾取动作，之后创建数据
        if (actionName.contains("Create"))
            actionOK = this->pickDataOper();
        //修改问题
        else if (actionName.contains("Edit"))
            actionOK = this->editLoadOper();
        
        return false;
    }

    bool OperatorsLoad::execProfession()
    {
        //获取数据id
        int objID = 0;
        if (!this->argValue("objID", objID)) return false;
        QString operType;
        if (!this->argValue<QString>("OperModelType", operType)) return false;
        //获取渲染数据
        EventOper::GraphEventOperator* graphOper = FITKOPERREPO->getOperatorT<EventOper::GraphEventOperator>("GraphPreprocess");
        if (graphOper == nullptr)return false;
        //是否需要初始化渲染数据
        bool forceUpdate = false;
        if(operType == "Create")
            forceUpdate = true;
        graphOper->updateGraph(objID, forceUpdate);

        //更新表格
        ModelOper::updateBrowserTable();

        return true;
    }

    bool OperatorsLoad::pickDataOper()
    {
        //获取主窗口和动作对象
        GUI::MainWindow * mainWindow = dynamic_cast<GUI::MainWindow *>(FITKAPP->getGlobalData()->getMainWindow());
        QAction* action = dynamic_cast<QAction*>(_emitter);
        if (!mainWindow || !action)return false;
        //获取选择
        bool isChecked = action->isChecked();
        if (!isChecked)
        {
            //清除拾取模式和高亮
            GUI::GUIPickInfo::ClearPickInfo();
            return false;
        }
        //创建类型
        int actionType = action->data().toInt();
        if (actionType == 0)
            //SEA 梁，板，声腔。FE 梁，板
            this->setArgs("LoadType", "Power");
        else if (actionType == 1)
            //SEA 梁，板。FE 梁，板
            this->setArgs("LoadType", "PointForce");
        else if (actionType == 2)
            //梁，板，声腔。FE
            this->setArgs("LoadType", "Constraint");
        else if (actionType == 3)
            //声腔
            this->setArgs("LoadType", "HVACNoise");
        else if (actionType == 4)
            //单极子
            this->setArgs("LoadType", "Monopole");
        //拾取界面
        GUI::GUILoadPickSubSysDialog* pickDialog = new GUI::GUILoadPickSubSysDialog(this, mainWindow);
        connect(pickDialog, &QDialog::accepted, this, &OperatorsLoad::pickDataFinished);
        pickDialog->show();
        return true;
    }

    bool OperatorsLoad::editLoadOper()
    {
        GUI::MainWindow * mainWindow = dynamic_cast<GUI::MainWindow *>(FITKAPP->getGlobalData()->getMainWindow());
        if (!mainWindow)return false;
        int dataID = 0;
        if (!this->argValue("DataID", dataID))return false;
        this->setArgs("OperModelType", "Edit");
        //获取物理数据
        Acoustics::FITKAcousticsPhysics* phyMgr = FITKAPP->getGlobalData()->getPhysicsData<Acoustics::FITKAcousticsPhysics>();
        if (!phyMgr) return false;
        //获取载荷管理器
        Acoustics::FITKAcousticsLoadManager* loadMgr = phyMgr->getFITKAcousticsLoadManager();
        if (!loadMgr) return false;
        Acoustics::FITKAcousticsAbstractLoad* loadData = loadMgr->getDataByID(dataID);
        if (!loadData) return false;
        Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType loadType = loadData->getAcousticsLoadType();
        //通过类型创建窗口
        QDialog* dialog = nullptr;
        if (loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ConstraintFESubsys || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ConstraintSEAAcoustic
            || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ConstraintSEABeam || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ConstraintSEAShell)
            dialog = new GUI::GUILoadConstraintDialog(loadData, this, mainWindow);
        else if (loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::PowerFESubsys || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::PowerSEAAcoustic
            || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::PowerSEABeam || loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::PowerSEAShell)
            dialog = new GUI::GUILoadPowerDialog(loadData, this, mainWindow);
        else if (loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ExcitationForceMoments)
            dialog = new GUI::GUILoadPointForceDialog(loadData, this, mainWindow);
        else if (loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ImportNoiseHVAC)
            dialog = new GUI::GUILoadHVACNoiseDialog(loadData, this, mainWindow);
        else if (loadType == Acoustics::FITKAcousticsAbstractLoad::AcousticsLoadType::ExcitationMonopole)
            dialog = new GUI::GUILoadMonopoleDialog(loadData, this, mainWindow);
        if (!dialog) return false;
        connect(dialog, &QDialog::accepted, this, &OperatorsLoad::execProfession);
        dialog->show();
        return true;
    }

    void OperatorsLoad::pickDataFinished()
    {
        QAction* action = dynamic_cast<QAction*>(_emitter);
        if (action)
            action->setChecked(false);
        //获取三维可视化对象管理
        GraphData::PickedDataProvider* pickProvider = GraphData::PickedDataProvider::getInstance();
        if (!pickProvider)return;
        //获取拾取数据对象
        QList<GraphData::PickedData*> pickList = pickProvider->getPickedList();
        int objID = -1;
        double connectPoint[3] = { 0,0,0 };
        //设置板
        GraphData::PickedDataType pickType;
        QList<GraphData::PickedFaceData> pickFaceList = {};
        QList<int> pickDataIndex{};
        for (GraphData::PickedData* pickData : pickList)
        {
            if (pickData == nullptr)continue;
            pickType = pickData->getPickedDataType();
            objID = pickData->getPickedDataObjId();
            pickDataIndex = pickData->getPickedIds();
            pickFaceList = pickData->getPickedSEAFaces();
        }
        if (objID == -1)
        {
            AppFrame::FITKMessageError("Load Create Err !!! pick data Err !");
            return;
        }
        //获取连接位置
        this->setArgs("PickObjID", objID);
        if (pickType == GraphData::PickedDataType::FEStructureShellPick || pickType == GraphData::PickedDataType::FEStructureBeamPick 
            || pickType == GraphData::PickedDataType::FEAcousticPick)
        {
            if (pickFaceList.isEmpty()) return;
            int elementID = pickFaceList.at(0).ElementId;
            int nodeID = -1;
            this->calculateFELocationPoint(elementID, connectPoint, nodeID);
            this->setArgs("NodeID", nodeID);
        }
        else if (pickType == GraphData::PickedDataType::SEABeamPick)
        {
            int pickElemIndex = -1;
            if (pickDataIndex.isEmpty()) return;
            pickElemIndex = pickDataIndex[0];
            this->calculateBeamLocationPoint(objID, connectPoint, pickElemIndex);
        }
        else if (pickType == GraphData::PickedDataType::SEAShellPick)
        {
            int pickElemIndex = -1;
            if (pickFaceList.isEmpty()) return;
            this->calculateFaceLocationPoint(pickFaceList[0], connectPoint, pickElemIndex);
        }
        else if (pickType == GraphData::PickedDataType::SEACavityPick)
        {
            int pickElemIndex = -1;
            if (pickFaceList.isEmpty()) return;
            this->calculateFaceLocationPoint(pickFaceList[0], connectPoint, pickElemIndex);
        }

        this->setArgs("connectPointX", connectPoint[0]);
        this->setArgs("connectPointY", connectPoint[1]);
        this->setArgs("connectPointZ", connectPoint[2]);
        createLoadOper();
    }

    void OperatorsLoad::createLoadOper()
    {
        //获取主窗口
        GUI::MainWindow * mainWindow = dynamic_cast<GUI::MainWindow *>(FITKAPP->getGlobalData()->getMainWindow());
        if (!mainWindow) return;
        //获取创建类型
        QString type;
        if (!this->argValue<QString>("LoadType", type)) return;
        this->setArgs("OperModelType", "Create");
        QDialog* dialog = nullptr;
        if (type == "Constraint")
            dialog = new GUI::GUILoadConstraintDialog(this, mainWindow);
        else if (type == "Power")
            dialog = new GUI::GUILoadPowerDialog(this, mainWindow);
        else if (type == "PointForce")
            dialog = new GUI::GUILoadPointForceDialog(this, mainWindow);
        else if (type == "HVACNoise")
            dialog = new GUI::GUILoadHVACNoiseDialog(this, mainWindow);
        else if (type == "Monopole")
            dialog = new GUI::GUILoadMonopoleDialog(this, mainWindow);
        if (!dialog) return;
        connect(dialog, &QDialog::accepted, this, &OperatorsLoad::execProfession);
        dialog->show();
    }

    void OperatorsLoad::calculateFaceLocationPoint(GraphData::PickedFaceData pickData, double * point, int& elemIndex)
    {
        Acoustics::FITKAcousticsDiscModel* discModel = FITKAPP->getGlobalData()->getMeshData<Acoustics::FITKAcousticsDiscModel>();
        if (discModel == nullptr && discModel->getSEAModel() == nullptr)return;
        Acoustics::FITKSEAFaceManager* faceManager = discModel->getSEAModel()->getSEAFaceManager();
        if (faceManager == nullptr)return;
        Acoustics::FITKAcousticsDiscNodeList* nodeList = discModel->getNodes();
        if (nodeList == nullptr)return;

        int faceID = pickData.FaceId;
        elemIndex = pickData.ElementId;

        QList<int> allNodes = {};
        Acoustics::FITKAbstractSEAFace* face = faceManager->getDataByID(faceID);
        if (face != nullptr) {
            Acoustics::FITKAcousticsTriangles* eleS = face->getTriangleMesh();
            if (eleS != nullptr && eleS->getElementCount() > elemIndex) {
                Interface::FITKAbstractElement* ele = eleS->getElementAt(elemIndex);
                if (ele == nullptr) return;
                allNodes = ele->getAllNodes();
            }
        }
        if (allNodes.size() == 0)return;

        QList<Core::FITKNode*> points = {};
        for (int nodeID : allNodes) {
            Core::FITKNode* point = nodeList->getNodeByID(nodeID);
            if (point == nullptr)continue;
            double node[3] = { 0,0,0 };
            points.append(point);
        }
        if (points.size() != 3)return;
        
        point[0] = (points[0]->x() + points[1]->x() + points[2]->x()) / 3;
        point[1] = (points[0]->y() + points[1]->y() + points[2]->y()) / 3;
        point[2] = (points[0]->z() + points[1]->z() + points[2]->z()) / 3;
    }

    void OperatorsLoad::calculateBeamLocationPoint(int objID, double* point, int elemIndex)
    {
        Acoustics::FITKAcousticsDiscModel* discModel = FITKAPP->getGlobalData()->getMeshData<Acoustics::FITKAcousticsDiscModel>();
        if (discModel == nullptr && discModel->getSEAModel() == nullptr)return;
        Acoustics::FITKAcousticsSEASubSysManager* seaMgr = discModel->getSEAModel()->getSEASubSysManager();
        Acoustics::FITKAcousticsDiscNodeList* nodeList = discModel->getNodes();
        if (seaMgr == nullptr || nodeList == nullptr)return;
        Acoustics::FITKAbstractSEASubSysWithNodes* data = dynamic_cast<Acoustics::FITKAbstractSEASubSysWithNodes*>(seaMgr->getDataByID(objID));
        QList<int> nodeIDs =  data->getNodeIDs();
        if (nodeIDs.size() < elemIndex + 2 || elemIndex < 0) return;
        Core::FITKNode* point1 = nodeList->getNodeByID(nodeIDs.at(elemIndex));
        Core::FITKNode* point2 = nodeList->getNodeByID(nodeIDs.at(elemIndex + 1));
        if (!point1 || !point2) return;
        point[0] = (point1->x() + point2->x()) / 2;
        point[1] = (point1->y() + point2->y()) / 2;
        point[2] = (point1->z() + point2->z()) / 2;
    }

    void OperatorsLoad::calculateFELocationPoint(int elementID, double* point, int& nodeID)
    {
        //获取节点和单元数据
        Acoustics::FITKAcousticsDiscModel* discModel = FITKAPP->getGlobalData()->getMeshData<Acoustics::FITKAcousticsDiscModel>();
        if (discModel == nullptr && discModel->getFEModel() == nullptr)return;
        Acoustics::FITKAcousticsDiscNodeList* nodeList = discModel->getNodes();
        Acoustics::FITKAcousticsFESubSysElementList* elementList = discModel->getFEModel()->getFESubSysElementList();
        if (!nodeList || !elementList) return;
        //获取单元
        Interface::FITKAbstractElement* element = elementList->getElementByID(elementID);
        if (!element && element->getNodeCount() <= 0) return;
        nodeID = element->getNodeID(0);
        Core::FITKNode* nodeData = nodeList->getNodeByID(nodeID);
        if (!nodeData) return;
        point[0] = nodeData->x();
        point[1] = nodeData->y();
        point[2] = nodeData->z();
    }
}

